package com.winsdom.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.winsdom.dto.LoginDto;
import com.winsdom.dto.ResetPasswordDTO;
import com.winsdom.entity.SysUser;
import com.winsdom.exception.BaseException;
import com.winsdom.mapper.LoginMapper;
import com.winsdom.mapper.SysUserMapper;
import com.winsdom.properties.JwtProperties;
import com.winsdom.service.IOrganizationService;
import com.winsdom.service.IResourceService;
import com.winsdom.service.LoginService;
import com.winsdom.service.ProjectManagementService;
import com.winsdom.utils.JwtUtil;
import com.winsdom.utils.PasswordUtil;
import com.winsdom.vo.LoginVo;
import com.winsdom.context.BaseContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.winsdom.enums.RedisKey.USER_PERMISSION;

@Service
@Slf4j
public class LoginServiceImpl implements LoginService {
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private  JwtProperties jwtProperties;
    @Autowired
    private LoginMapper loginMapper;
    @Autowired
    private ProjectManagementService projectManagementService;
    @Autowired
    private IOrganizationService iOrganizationService;
    @Autowired
    private IResourceService resourceService;

    // Redis键前缀
    private static final String LOCK_KEY_PREFIX = "account_lock:";
    private static final String ATTEMPTS_KEY_PREFIX = "login_attempts:";
    private static final String VERIFICATION_CODE_KEY_PREFIX = "reset_password_verification_code:";
    // 验证码有效期（单位：分钟）
    private static final long VERIFICATION_CODE_EXPIRE_TIME = 5;

    // 锁定时间配置（单位：分钟）
    private static final long LOCK_DURATION = 5;
    private static final int MAX_ATTEMPTS = 5;

    @Override
    public LoginVo login(LoginDto loginDTO) {
        //验证用户名密码是否正确
        LoginVo loginVo = new LoginVo();
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        // 验证用户名密码是否正确
        // 1. 查询用户信息
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getUsername, username);
        wrapper.eq(SysUser::getStatus, 1);
        SysUser user = sysUserMapper.selectOne(wrapper);
        if (user == null) {
            throw new BaseException("账号错误");
        }
        //验证redis是否有锁定
        if (isAccountLocked(user.getId())) {
            throw new BaseException("账号已锁定，请5分钟后再试");
        }
        //验证密码是否正确
        boolean a =PasswordUtil.matches(password, user.getPassword());
        if (!a)
        {
            //不正确，进入redis记录尝试次数,如果超过五次锁定
            handleFailedLogin(user.getId());
            throw new BaseException("密码错误");
        }
        try {
            // 生成token
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", user.getId());
            claims.put("username", user.getUsername());
            claims.put("phone", user.getPhone());
            claims.put("name", user.getName());
            claims.put("categoryId",user.getCategoryId());
            String token = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl() * 60 * 1000, claims);
            loginVo.setToken(token);
            loginVo.setSysUser(user);
            
            // 将用户ID存入ThreadLocal
            BaseContext.setCurrentId(user.getId().intValue());
            BaseContext.setCategoryId(user.getCategoryId().intValue());
            //查询组织所有子项目 存入redis
            projectManagementService.getOrganizationTree();
            //查询组织树 存入redis
            iOrganizationService.getOrganizationTree();
            //查询组织+资源 存入redis
            resourceService.getOrganizationTree();

            //往redis里面放权限
            //根据userid查询权限，自定义方法链表。
            List<String> permissionCodes = loginMapper.getPermissionCodes(user.getId());
            log.info("permissionCodes:{}", permissionCodes);
            savePermissionsToRedis(user.getId(), permissionCodes);

            //清除锁定
            resetLoginStatus(loginVo.getSysUser().getId());

            return loginVo;
        }catch (Exception e) {
            throw new BaseException(e.getMessage());
        }

    }

    @Override
    public List<String> getPermissionCodes(Integer userId) {
        return Collections.emptyList();
    }

    //锁定方法
    private boolean isAccountLocked(Long userId) {
        return redisTemplate.hasKey(LOCK_KEY_PREFIX + userId);
    }
    //进行判断
    private void handleFailedLogin(long userId) {
        // 使用Lua脚本保证原子操作
        String script =
                "local attemptsKey = KEYS[1]\n" +
                        "local lockKey = KEYS[2]\n" +
                        "local attempts = redis.call('INCR', attemptsKey)\n" +
                        "if attempts == 1 then\n" +
                        "   redis.call('EXPIRE', attemptsKey, 3600)\n" + // 失败计数器保留1小时
                        "end\n" +
                        "if attempts >= tonumber(ARGV[1]) then\n" +
                        "   redis.call('SETEX', lockKey, ARGV[2], '1')\n" + // 设置锁定
                        "   redis.call('DEL', attemptsKey)\n" + // 清除计数器
                        "end\n" +
                        "return attempts";

        RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        List<String> keys = java.util.Arrays.asList(
                ATTEMPTS_KEY_PREFIX + userId,
                LOCK_KEY_PREFIX + userId
        );

        Long attempts = redisTemplate.execute(
                redisScript,
                keys,
                MAX_ATTEMPTS,
                LOCK_DURATION * 60
        );

        if (attempts != null && attempts >= MAX_ATTEMPTS) {
            throw new BaseException("连续登录失败，账号已锁定");
        }
    }
    //清除锁定次数和状态
    private void resetLoginStatus(Long userId) {
        // 清除Redis中的相关记录
        redisTemplate.delete(ATTEMPTS_KEY_PREFIX + userId);
        redisTemplate.delete(LOCK_KEY_PREFIX + userId);
    }
    //保存权限
    private void savePermissionsToRedis(Long userId, List<String> permissionCodes) {
        String redisKey = USER_PERMISSION.getValue()+userId;
        // 先清空之前的权限信息（如果有）
        redisTemplate.delete(redisKey);
        // 将权限码存储到 Redis 的列表中
        redisTemplate.opsForValue().set(redisKey, permissionCodes, 1, TimeUnit.HOURS);
    }

    //修改密码
    @Override
    public void resetPassword(ResetPasswordDTO resetPasswordDTO) {
        //查询密码 验证密码是否正确
        String username = resetPasswordDTO.getUsername();
        String oldPassword = resetPasswordDTO.getOldPassword();
        String newPassword = resetPasswordDTO.getNewPassword();
        //查询密码
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getUsername, username);
        SysUser user = sysUserMapper.selectOne(wrapper);
        if (user == null) {
            new BaseException("账号错误");
        }
        //验证密码
        Boolean a = PasswordUtil.matches(oldPassword, user.getPassword());
        if (!a) {
            new BaseException("原密码错误");
        }
        //验证旧密码和新密码不能一眼
        if(oldPassword.equals(newPassword))
            throw new BaseException("新密码不能与旧密码相同");
        //新密码加密
        user.setPassword(PasswordUtil.encode(newPassword));
        //跟新
        sysUserMapper.updateById(user);
    }
}